﻿using EYangFramework.Common.BasicHelper;
using EYangFramework.Domain.Entitys;
using EYangFramework.Domain.Repositorys;
using Oracle.ManagedDataAccess.Client;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using static EYangFramework.Common.BasicHelper.SerializeHelper;

namespace EYangFramework.Domain.Repositorys
{

    public class Repository<TEntity> : IRepository<TEntity> where TEntity : class, IEntity
    {
        public DbContext DbContext { get; set; }
        DbSet<TEntity> Table;
        SerializeHelper serializeHelper;
        public Repository(DbContext dbContext)
        {
            this.DbContext = dbContext;
            this.Table = DbContext.Set<TEntity>();
            serializeHelper = new SerializeHelper();
        }
        public void Delete(TEntity entity)
        {
            //AttachIfNot(entity);
            //DbContext.Entry<TEntity>(entity).State = EntityState.Deleted;
            try
            {
                DbContext.SaveChanges();
            }
            catch (OracleException)
            {
                WriteEntityToTemp(entity, 2);
            }
        }

        public void Delete(Guid id)
        {
            TEntity entity = Get(id);
            //AttachIfNot(entity);
            Table.Remove(entity);
            try
            {
                DbContext.SaveChanges();
            }
            catch (OracleException)
            {
                WriteEntityToTemp(entity, 2);
            }
        }

        public void Delete(Expression<Func<TEntity, bool>> predicate)
        {
            List<TEntity> entities = GetAllList(predicate);
            using (DbContextTransaction transaction = DbContext.Database.BeginTransaction())
            {
                try
                {
                    foreach (var entity in entities)
                    {
                        Table.Remove(entity);
                        //AttachIfNot(entity);
                        //DbContext.Entry<TEntity>(entity).State = EntityState.Deleted;
                    }
                    DbContext.SaveChanges();
                    transaction.Commit();
                }
                catch (OracleException)
                {
                    WriteEntitiesToTemp(entities, 2);
                    transaction.Rollback();
                }
                catch (Exception)
                {
                    transaction.Rollback();
                }
            }
        }

        public Task DeleteAsync(TEntity entity)
        {
            Delete(entity);
            return Task.FromResult(0);
        }

        public Task DeleteAsync(Guid id)
        {
            Delete(id);
            return Task.FromResult(0);
        }

        public Task DeleteAsync(Expression<Func<TEntity, bool>> predicate)
        {
            Delete(predicate);
            return Task.FromResult(0);
        }

        public TEntity FirstOrDefault(Guid id)
        {
            return Table.Where(t => t.ID == id).FirstOrDefault();
        }

        public TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate)
        {
            return Table.Where(predicate).FirstOrDefault();
        }

        public Task<TEntity> FirstOrDefaultAsync(Guid id)
        {
            return Table.Where(t => t.ID == id).FirstOrDefaultAsync();
        }

        public Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate)
        {
            return Table.Where(predicate).FirstOrDefaultAsync();
        }

        public TEntity Get(Guid id)
        {
            return Table.Where(t => t.ID == id).First();
        }

        public IQueryable<TEntity> GetAll()
        {
            return Table;
        }
        public List<TEntity> GetAllList()
        {
            return GetAll().ToList();
        }

        public List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate)
        {
            return Table.Where(predicate).ToList();
        }

        public Task<List<TEntity>> GetAllListAsync()
        {
            return Task.FromResult(GetAllList());
        }

        public Task<List<TEntity>> GetAllListAsync(Expression<Func<TEntity, bool>> predicate)
        {
            return Task.FromResult(GetAllList(predicate));
        }

        public Task<TEntity> GetAsync(Guid id)
        {
            return Task.FromResult(Get(id));
        }

        public TEntity Insert(TEntity entity)
        {
            //AttachIfNot(entity);
            //DbContext.Entry<TEntity>(entity).State = EntityState.Added;
            Table.Add(entity);
            try
            {
                DbContext.SaveChanges();
            }
            catch (OracleException)
            {
                WriteEntityToTemp(entity, 0);
            }
            return entity;
        }

        public void Insert(List<TEntity> entities)
        {
            using (DbContextTransaction transaction = DbContext.Database.BeginTransaction())
            {
                try
                {
                    foreach (var entity in entities)
                    {
                        Table.Add(entity);
                        //AttachIfNot(entity);
                        //DbContext.Entry<TEntity>(entity).State = EntityState.Added;
                    }
                    DbContext.SaveChanges();
                    transaction.Commit();
                }
                catch (OracleException)
                {
                    WriteEntitiesToTemp(entities, 0);
                    transaction.Rollback();
                }
                catch (Exception)
                {
                    transaction.Rollback();
                }
            }
        }

        public Guid InsertAndGetId(TEntity entity)
        {
            Insert(entity);
            return entity.ID;
        }

        public Task<Guid> InsertAndGetIdAsync(TEntity entity)
        {
            return Task.FromResult(InsertAndGetId(entity));
        }

        public Task<TEntity> InsertAsync(TEntity entity)
        {
            return Task.FromResult(Insert(entity));
        }

        public Task InsertAsync(List<TEntity> entities)
        {
            Insert(entities);
            return Task.FromResult(0);
        }

        public T Query<T>(Func<IQueryable<TEntity>, T> queryMethod)
        {
            return queryMethod(GetAll());
        }

        public TEntity Single(Expression<Func<TEntity, bool>> predicate)
        {
            return GetAll().Single(predicate);
        }

        public Task<TEntity> SingleAsync(Expression<Func<TEntity, bool>> predicate)
        {
            return GetAll().SingleAsync(predicate);
        }

        public TEntity Update(TEntity entity)
        {
            //AttachIfNot(entity);
            //DbContext.Entry<TEntity>(entity).State = EntityState.Modified;
            try
            {
                DbContext.SaveChanges();
            }
            catch (OracleException)
            {
                WriteEntityToTemp(entity, 1);
            }
            return entity;
        }

        public TEntity Update(Guid id, Action<TEntity> updateAction)
        {
            TEntity entity = Get(id);
            updateAction(entity);
            //DbContext.Entry<TEntity>(entity).State = EntityState.Modified;
            try
            {
                DbContext.SaveChanges();
            }
            catch (OracleException)
            {
                WriteEntityToTemp(entity, 1);
            }
            return entity;
        }

        public Task<TEntity> UpdateAsync(TEntity entity)
        {
            return Task.FromResult(Update(entity));
        }

        public List<TElement> ExcuteQuerySql<TElement>(string sql, params object[] parameters)
        {
            return DbContext.Database.SqlQuery<TElement>(sql, parameters).ToList();
        }

        public int ExecuteSqlNoQuery(string sql, params object[] parameters)
        {
            return DbContext.Database.ExecuteSqlCommand(sql, parameters);
        }
        private bool AttachIfNot(TEntity entity)
        {
            if (!Table.Local.Contains(entity))
            {
                Table.Attach(entity);
                return true;
            }
            return false;
        }
        private void WriteEntitiesToTemp(List<TEntity> entities, int state)
        {
            List<TempObject> list = new List<TempObject>();
            foreach (var entity in entities)
            {
                TempObject tempObject = new TempObject(typeof(TEntity), entity, state);
                list.Add(tempObject);
            }
            serializeHelper.SerializerObjectToTemp(list);
        }
        private void WriteEntityToTemp(TEntity entity, int state)
        {
            List<TEntity> entities = new List<TEntity>();
            entities.Add(entity);
            WriteEntitiesToTemp(entities, state);
        }

        public IQueryable<TEntity> GetAllNoTracking()
        {
            return Table.AsNoTracking();
        }

        public List<TEntity> GetAllListNoTracking(Expression<Func<TEntity, bool>> predicate)
        {
            return Table.AsNoTracking().Where(predicate).ToList();
        }
    }
}
